//
// Created by fanghr on 18-3-29.
//

#include <opencv2/opencv.hpp>
#include "binder_cpp.h"
#include "ashmem_cpp.h"
#include <sys/mman.h>

void *cap_thread(void *);

void *copy_to_ashmem(void *param);

class cap_thread_param {
public:
    std::string path;
    int device;
    cv::Mat &frame;
    pthread_mutex_t mutex, mutex_copy;
    pthread_t thread_cap, thread_copy;
    size_t cols, rows;
    void *raw_ptr;

    cap_thread_param(cv::Mat &_frame, std::string str) : frame(_frame) {
        device = -1;
        path = str;
        cols = rows = 0;
        pthread_mutex_init(&mutex, nullptr);
        pthread_mutex_init(&mutex_copy, nullptr);
    }

    cap_thread_param(cv::Mat &_frame, int dev) : frame(_frame) {
        device = dev;
        cols = rows = 0;
        pthread_mutex_init(&mutex, nullptr);
        pthread_mutex_init(&mutex_copy, nullptr);
    }

    int run_cap_thread() {
        return pthread_create(&thread_cap, nullptr, cap_thread, (void *) this);
    }

    int run_copy_to_ashmem() {
        return pthread_create(&thread_copy, nullptr, copy_to_ashmem, (void *) this);
    }
};

void *cap_thread(void *param) {
    bool is_first_time = true;
    auto obj = *(cap_thread_param *) param;
    cv::VideoCapture cap;
    if (obj.device >= 0)
        cap.open(obj.device);
    else
        cap.open(obj.path);

    if (!cap.isOpened()) {
        int *return_value = new int(-1);
        pthread_exit((void *) return_value);
    }

    while (true) {
        pthread_mutex_lock(&obj.mutex);
        cap >> obj.frame;
        if (is_first_time) {
            obj.cols = obj.frame.cols;
            obj.rows = obj.frame.rows;
            is_first_time = !is_first_time;
        }
        pthread_mutex_unlock(&obj.mutex);
    }

//    auto return_value = new int(0);
//    pthread_exit((void *) return_value);
}

void *copy_to_ashmem(void *param) {
    auto obj = *(cap_thread_param *) param;
    while (true) {
        pthread_mutex_lock(&obj.mutex);
        pthread_mutex_lock(&obj.mutex_copy);
        if (obj.frame.isContinuous())
            memcpy(obj.raw_ptr, obj.frame.data, obj.cols * obj.rows);
        pthread_mutex_unlock(&obj.mutex);
        pthread_mutex_unlock(&obj.mutex_copy);
    }
}

int
handler(struct binder_state *bs, struct binder_transaction_data *txn, struct binder_io *msg, struct binder_io *reply) {}

int main() {
    cv::Mat mat_l, mat_r;

    cap_thread_param param_l(mat_l, 0), param_r(mat_r, 1);

    if (param_l.run_cap_thread() ||
        param_r.run_cap_thread())
        return -1;

    pthread_mutex_lock(&param_l.mutex);
    ashmem_cpp::ashmem mat_l_ashmem("raw_l", param_l.rows * param_l.cols);
    ashmem_cpp::ashmem mat_r_ashmem("raw_r", param_l.rows * param_l.cols);
    pthread_mutex_unlock(&param_l.mutex);

    mat_l_ashmem.set_prot_region(PROT_READ | PROT_WRITE);
    mat_r_ashmem.set_prot_region(PROT_READ | PROT_WRITE);
    int fd_l = ashmem_cpp::access::get_fd(mat_l_ashmem), fd_r = ashmem_cpp::access::get_fd(mat_r_ashmem);

    void *raw_ptr_l = mmap(nullptr, mat_l_ashmem.get_size_region(), PROT_READ | PROT_WRITE, MAP_SHARED, fd_l, 0);
    void *raw_ptr_r = mmap(nullptr, mat_l_ashmem.get_size_region(), PROT_READ | PROT_WRITE, MAP_SHARED, fd_r, 0);

    param_l.raw_ptr = raw_ptr_l;
    param_r.raw_ptr = raw_ptr_r;

    if (param_l.run_copy_to_ashmem() || param_r.run_copy_to_ashmem())
        return -1;

    binder_cpp::binder binder(512 * 1024);
    binder_cpp::server server(binder);

    server.publish(BINDER_SERVICE_MANAGER, const_cast<char *>("camcap_svc"), nullptr);
    server.loop(handler);

    return 0;
}